home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld: Complete Mac Interactive
/
Macworld Complete Mac Interactive CD)(1994).iso
/
The Best of BMUG
/
Entertainment
/
Strategy
/
Bolo
/
Bolo Utilities
/
BoloBrains
/
Terminator Brain
/
terminator.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-09-28
|
32KB
|
1,137 lines
/*--------------------------------------------------------------------------------
Copyright ©1993 Steve Israelson
All rights reserved.
Permisson granted for personal use only.
--------------------------------------------------------------------------------*/
#include <FixMath.h>
#include <Menus.h>
#include <Events.h>
#include <Resources.h>
#include <stdarg.h>
#include "debug.h"
#include "Brain.h"
#include "terminator.proto.h"
extern int vsprintf(char *sbuffer, char *fmt, va_list arg);
#define MyMenuID 1000
#define MIN_SPEED 10
#define NICE_LAND (1 << ROAD | 1 << FOREST | 1 << GRASS)
#define WATER (1 << RIVER | 1 << SWAMP | 1 << CRATER)
#define OBSTACLE (1 << BUILDING | 1 << RUBBLE | 1 << HALFBUILDING)
#define LAND (NICE_LAND | WATER | OBSTACLE)
#define UNITS (1 << BOAT | 1 << REFBASE_T | 1 << PILLBOX_T)
#define clearkey(CONTROLVECTOR, COMMAND) CONTROLVECTOR &= ~(1<<COMMAND)
#define MAX_MSGS 16
#define MIN_WAIT (60 * 2)
#define PILL_MSG 0
#define BASE_MSG 1
#define MOVE_MSG 2
long thinkStart; /* start time for thinking */
long lastMsg, /* the last time a message was displayed */
lastObstructed; /* the last time we were obstructed */
BrainInfo *brainInfo; /* the info packet */
MenuHandle MyMenu; /* the options menu */
Boolean aggressive; /* menu option */
Boolean laymines; /* menu option */
Boolean clearmines; /* menu option */
Boolean placepills; /* menu option */
u_long newKeys, newTaps, /* new keys to press or tap */
lastKeys, lastTaps; /* last keys we pressed or tapped */
short repeatLastKeys; /* should we repeat the last key? if so how many times */
long msgTimes[MAX_MSGS]; /* timers for the different msg types */
ObjectInfo *closestShot; /* the closest shot to us */
short incoming_shells; /* how many incomming shells */
WORLD_X lastSeenPillx; /* last pillbox we have seen */
WORLD_Y lastSeenPilly;
WORLD_X basesx[16]; /* array of all the bases we have seen */
WORLD_Y basesy[16];
// For Mac fixed point trig routines, full circle is 2 * PI * 0x10000
// We want full circle to be 0x100, so divide by (2 * PI * 0x10000 / 0x100)
// which is 1608 in decimal
/* assume us at 0,0 return angle to x,y (in fixed) */
#define aim(X,Y) ((FixATan2(-(Y),(X))/1608) & 0xFF)
// sin and cos routines are adjusted to take angles in the range 0-255
// and return return values in the range +/-128
#define sin(X) ((short)(FracSin((u_char)(X)*1608L) >> 23))
#define cos(X) ((short)(FracCos((u_char)(X)*1608L) >> 23))
#define rangeFromMe(OB) findrange((OB)->x, (OB)->y, brainInfo->tankx, brainInfo->tanky)
/*--------------------------------------------------------------------------------
The main brain killing machaine
--------------------------------------------------------------------------------*/
pascal short main(BrainInfo *theInfo)
{
if (theInfo->InfoVersion != CURRENT_BRAININFO_VERSION)
return -1;
brainInfo = theInfo;
thinkStart = TickCount();
switch (brainInfo->operation)
{
case BRAIN_OPEN:
if (openBrain())
return 0;
closeBrain();
return -1;
break;
case BRAIN_CLOSE:
closeBrain();
return 0;
break;
case BRAIN_THINK:
think();
return 0;
break;
case MyMenuID:
return(doMenu(brainInfo->menu_item));
break;
default:
return 0;
break;
}
}
/*--------------------------------------------------------------------------------
Init all our globals. Set up menus.
--------------------------------------------------------------------------------*/
Boolean openBrain(void)
{
short x;
aggressive = laymines = clearmines = placepills = true;
lastKeys = lastTaps = lastObstructed = 0;
repeatLastKeys = 0;
lastSeenPilly = lastSeenPillx = 0;
for (x = 0; x < 16; ++x)
basesx[x] = basesy[x] = 0;
lastMsg = TickCount();
InsertMenu(MyMenu = GetMenu(MyMenuID), 0);
DrawMenuBar();
CheckItem(MyMenu, 1, aggressive);
CheckItem(MyMenu, 2, laymines);
CheckItem(MyMenu, 3, clearmines);
CheckItem(MyMenu, 4, placepills);
for (x = 0; x < MAX_MSGS; ++x)
msgTimes[x] = 0;
return true;
}
/*--------------------------------------------------------------------------------
Trash our menus.
--------------------------------------------------------------------------------*/
void closeBrain(void)
{
DeleteMenu(MyMenuID);
ReleaseResource((Handle)MyMenu);
DrawMenuBar();
}
/*--------------------------------------------------------------------------------
Do a menu, maybe someday actually do something here.
--------------------------------------------------------------------------------*/
short doMenu(short item)
{
switch (item)
{
case 1:
CheckItem(MyMenu, 1, aggressive ^= 1);
break;
case 2:
CheckItem(MyMenu, 2, laymines ^= 1);
break;
case 3:
CheckItem(MyMenu, 3, clearmines ^= 1);
break;
case 4:
CheckItem(MyMenu, 4, placepills ^= 1);
break;
default:
return -1;
}
return 0;
}
/*--------------------------------------------------------------------------------
I think, therfore I kill. Do man actions, then move actions, then others.
--------------------------------------------------------------------------------*/
void think(void)
{
newKeys = 0;
newTaps = 0;
countIncommingShots();
if (!doMan()) /* place pills, lay mines */
doMove();
doOther();
*(brainInfo->holdkeys) = newKeys;
*(brainInfo->tapkeys) = newTaps;
lastKeys = newKeys;
lastTaps = newTaps;
}
/*--------------------------------------------------------------------------------
Move our tank, sitting still is useless unless we are refuling.
You should rank these according the priority you think these are.
--------------------------------------------------------------------------------*/
Boolean doMove(void)
{
if (avoidDeepWater())
return true;
if (avoidMines())
return true;
if (checkObstructions())
return true;
if (avoidShots())
return true;
if (avoidPill())
return true;
if (avoidEnemy())
return true;
if (getMan())
return true;
if (avoidWater())
return true;
if (approachEnemyPill())
return true;
if (approachBase())
return true;
if (approachEnemyBase())
return true;
selectRoute();
/* if nothing to do then speed up! Places to go etc.. */
speedUp();
return false;
}
/*--------------------------------------------------------------------------------
Refuel if we are low. Capture nearby neutral bases.
--------------------------------------------------------------------------------*/
Boolean approachBase(void)
{
long range;
ObjectInfo *ob;
short x, closestBase = -1;
long minDistance = 0x7fffffff;
Boolean found;
if (!brainInfo->base) /* no base nearby so look for some farther away */
{
for (ob = &brainInfo->objects[0]; ob < &brainInfo->objects[brainInfo->num_objects]; ob++)
{
if (ob->info & OBJECT_HOSTILE)
continue;
if (ob->object == OBJECT_REFBASE && ob->info & OBJECT_NEUTRAL)
{
range = rangeFromMe(ob);
if ((range >> 8) < 14)
{
if (nearestEnemyPill(ob->x, ob->y)) /* should check distance here ##### */
continue;
/*sendDebugMsg(BASE_MSG, "B%d,%d", (int)(ob->x >> 8), (int)(ob->y >> 8));*/
approachSpace(ob->x, ob->y);
return speedUp();
}
}
if (!brainInfo->carriedpills && brainInfo->armour > 7
&& brainInfo->shells > 20 && brainInfo->mines > 10)
continue;
}
/* no acceptable base nearby, so if we need it, lets find one we have been to before */
if (brainInfo->armour < 4)
{
for (x = 0; x < 16; ++x)
{
if (basesx[x] || basesy[x])
{
range = findrange(basesx[x], basesy[x], brainInfo->tankx, brainInfo->tanky);
if (range < minDistance)
{
minDistance = range;
closestBase = x;
}
}
}
if (closestBase != -1)
{
/* sendDebugMsg(BASE_MSG, "Seek RB %d,%d",basesx[closestBase] >> 8,basesy[closestBase] >> 8); */
approachSpace(basesx[closestBase], basesy[closestBase]);
speedUp();
return true;
}
}
return false;
}
/* nearby hostile base, ignore -- later maybee we should kill it */
if (brainInfo->base->info & OBJECT_HOSTILE)
return false;
/* are we on a base, if so sit still */
if (terrainSpot() == REFBASE_T)
{
found = false;
for (x = 0; x < 16; ++x)
{
if (basesx[x] || basesy[x])
continue;
if ((basesx[x] >> 8) == (brainInfo->tankx >> 8)
&& (basesy[x] >> 8) == (brainInfo->tanky >> 8))
{
found = true;
break;
}
}
if (!found) /* add to our database */
for (x = 0; x < 16; ++x)
{
if (basesx[x] || basesy[x])
continue;
basesx[x] = brainInfo->tankx;
basesy[x] = brainInfo->tanky;
/* sendDebugMsg(BASE_MSG, "RBS %d,%d", brainInfo->tankx >> 8, brainInfo->tanky >> 8); */
break;
}
slowDown();
/* stay on the base until fueled */
if (brainInfo->base_armour > MIN_BASE_ARMOUR && brainInfo->armour != 8)
return true;
if (brainInfo->base_shells && brainInfo->shells != 40)
return true;
if (brainInfo->base_mines && brainInfo->mines != 40)
return true;
/* lets get going, places to go things to kill */
speedUp();
return false;
}
/* approach the nearby base, unless there is an enemy pill nearby */
if (!(brainInfo->base->info & OBJECT_NEUTRAL)
&& !(brainInfo->carriedpills && brainInfo->man_status == 0)
&& brainInfo->armour > 7
&& brainInfo->shells > 20 && brainInfo->mines > 10)
return false;
if (brainInfo->base_armour <= MIN_BASE_ARMOUR || brainInfo->base_shells == 0
|| brainInfo->base_mines == 0)
return false;
if (nearestEnemyPill(brainInfo->base->x, brainInfo->base->y)) /* should check distance here ##### */
return false;
/*sendDebugMsg(BASE_MSG, "CB%d,%d", (int)(brainInfo->base->x >> 8), (int)(brainInfo->base->y >> 8));*/
approachSpace(brainInfo->base->x, brainInfo->base->y);
range = rangeFromMe(brainInfo->base);
if ((range >> 8) < 2 && brainInfo->speed > MIN_SPEED)
slowDown();
else
speedUp();
return true;
}
/*--------------------------------------------------------------------------------
Add code here to destroy!
--------------------------------------------------------------------------------*/
Boolean approachEnemyBase(void)
{
}
/*--------------------------------------------------------------------------------
Are we stuck? then get unstuck and get going, busy busy busy.
--------------------------------------------------------------------------------*/
Boolean checkObstructions(void)
{
TERRAIN nearSpace;
short x, y;
Boolean tryNewApproach = false; /* try something else cause were still stuck */
if (lastObstructed || brainInfo->tankobstructed)
{
sendDebugMsg(MOVE_MSG, "stk");
speedUp();
x = brainInfo->tankx >> 8;
y = brainInfo->tanky >> 8;
if (!lastObstructed)
lastObstructed = TickCount() + 60;
else if (lastObstructed < TickCount())
lastObstructed = 0;
if (lastObstructed && lastObstructed < TickCount() + 20)
tryNewApproach = true;
nearSpace = raw_getmapcell(x - 1, y) & TERRAIN_MASK;
if (nearSpace != BUILDING && nearSpace != HALFBUILDING && nearSpace != DEEPSEA)
{
if (!tryNewApproach)
{
approachSpace((x - 1) << 8, y << 8);
return speedUp();
}
tryNewApproach = false;
}
nearSpace = raw_getmapcell(x + 1, y) & TERRAIN_MASK;
if (nearSpace != BUILDING && nearSpace != HALFBUILDING && nearSpace != DEEPSEA)
{
if (!tryNewApproach)
{
approachSpace((x + 1) << 8, (y) << 8);
return speedUp();
}
tryNewApproach = false;
}
nearSpace = raw_getmapcell(x, y - 1) & TERRAIN_MASK;
if (nearSpace != BUILDING && nearSpace != HALFBUILDING && nearSpace != DEEPSEA)
{
if (!tryNewApproach)
{
approachSpace((x) << 8, (y - 1) << 8);
return speedUp();
}
tryNewApproach = false;
}
nearSpace = raw_getmapcell(x, y + 1) & TERRAIN_MASK;
if (nearSpace != BUILDING && nearSpace != HALFBUILDING && nearSpace != DEEPSEA)
{
if (!tryNewApproach)
{
approachSpace((x) << 8, (y + 1) << 8);
return speedUp();
}
tryNewApproach = false;
}
sendDebugMsg(MOVE_MSG, "fire");
if (!approachObject(TERRAIN_MASK, BUILDING, 1, 128))
approachObject(TERRAIN_MASK, HALFBUILDING, 1, 128);
setkey(newTaps, KEY_shoot);
return true;
}
return false;
}
/*--------------------------------------------------------------------------------
Select the best route to travel. Randomly turn for variety.
--------------------------------------------------------------------------------*/
Boolean selectRoute(void)
{
u_char x, y;
if (repeatLastKeys)
{
newKeys = lastKeys;
--repeatLastKeys;
return true;
}
if (rnd(0, 100) < 98)
{
/* if it is close, then why turn */
if (findObject(TERRAIN_MASK, ROAD, 1, 64, &x, &y))
return false;
if (findObject(TERRAIN_MASK, FOREST, 1, 64, &x, &y))
return false;
if (findObject(TERRAIN_MASK, GRASS, 1, 64, &x, &y))
return false;
/* if it is not close, but near, then turn */
if (approachObject(TERRAIN_MASK, ROAD, 4, 30))
return true;
if (approachObject(TERRAIN_MASK, FOREST, 4, 30))
return true;
if (approachObject(TERRAIN_MASK, GRASS, 4, 30))
return true;
if (approachObject(TERRAIN_MASK, ROAD, 14, 30))
return true;
if (approachObject(TERRAIN_MASK, FOREST, 14, 30))
return true;
if (approachObject(TERRAIN_MASK, GRASS, 14, 30))
return true;
}
if (rnd(0, 100) < 50)
setkey(newKeys, KEY_turnleft);
else
setkey(newKeys, KEY_turnright);
repeatLastKeys = 5;
}
/*--------------------------------------------------------------------------------
Approach an enemy pill and decimate it.
--------------------------------------------------------------------------------*/
Boolean approachEnemyPill(void)
{
ObjectInfo *ob;
u_long range;
short direction;
short da;
if (brainInfo->shells < 8)
return false;
for (ob = &brainInfo->objects[0]; ob < &brainInfo->objects[brainInfo->num_objects]; ob++)
{
if (ob->object == OBJECT_PILLBOX && (ob->info & OBJECT_HOSTILE))
{
if (brainInfo->armour <= 4 && ob->direction > 1) /* wimp out only if damaged */
continue;
range = rangeFromMe(ob);
approachSpace(ob->x, ob->y);
if ((range >> 8) >= 7)
{
/*sendDebugMsg(PILL_MSG, "P%d,%d", (int)(ob->x >> 8), (int)(ob->y >> 8));*/
speedUp();
}
else
if (ob->direction < 2)
{
/*sendDebugMsg(PILL_MSG, "P%d,%d", (int)(ob->x >> 8), (int)(ob->y >> 8));*/
speedUp();
}
else
slowDown();
if (ob->direction && range >> 8 <= 7) /* if not dead then kill! */
{
direction = aim((unsigned long)ob->x - (unsigned long)brainInfo->tankx,
(unsigned long)ob->y - (unsigned long)brainInfo->tanky);
da = brainInfo->direction - direction;
if (da < 4 && da > -4)
setkey(newTaps, KEY_shoot);
}
lastSeenPillx = ob->x;
lastSeenPilly = ob->y;
/*sendDebugMsg(PILL_MSG, "PS%d,%d", (int)(lastSeenPillx >> 8), (int)(lastSeenPilly >> 8));*/
return true;
}
}
if (brainInfo->armour > 4 && (lastSeenPilly || lastSeenPillx))
{
/*sendDebugMsg(PILL_MSG, "PS%d,%d", (int)(lastSeenPillx >> 8), (int)(lastSeenPilly >> 8));*/
approachSpace(lastSeenPillx, lastSeenPilly);
if (findrange(brainInfo->tankx, brainInfo->tanky, lastSeenPillx, lastSeenPilly) >> 8 < 15)
lastSeenPillx = lastSeenPilly = 0;
speedUp();
return true;
}
return false;
}
/*--------------------------------------------------------------------------------
Return the nearest enemy pill.
--------------------------------------------------------------------------------*/
ObjectInfo *nearestEnemyPill(WORLD_X x, WORLD_Y y)
{
ObjectInfo *ob, *closestPill;
u_long range, closest;
short direction;
short da;
closestPill = nil;
closest = 8 << 8; /* must be close enough to shoot */
for (ob = &brainInfo->objects[0]; ob < &brainInfo->objects[brainInfo->num_objects]; ob++)
{
if (ob->object == OBJECT_PILLBOX && (ob->info & OBJECT_HOSTILE))
{
range = findrange(ob->x, ob->y, x, y);
if (range < closest)
{
closest = range;
closestPill = ob;
}
}
}
return closestPill;
}
/*--------------------------------------------------------------------------------
Shoot at hostiles and walls and clear mines, farm build recover man etc.
--------------------------------------------------------------------------------*/
Boolean doOther(void)
{
doMan();
}
/*--------------------------------------------------------------------------------
If the man is far away then wait for him.
If he is very far away, then go get him.
--------------------------------------------------------------------------------*/
Boolean getMan(void)
{
short dx, dy;
long distance;
if (brainInfo->man_status > 1) /* outside tank and alive, for now */
{
dx = (brainInfo->tankx >> 8) - (brainInfo->man_x >> 8);
dy = (brainInfo->tanky >> 8) - (brainInfo->man_y >> 8);
distance = dx * dx + dy * dy;
if (brainInfo->manobstructed == 2 || distance > 100) /* go get him */
{
approachSpace(brainInfo->man_x, brainInfo->man_y);
return speedUp();
}
if (distance > 25)
return slowDown();
}
return false;
}
/*--------------------------------------------------------------------------------
Do things with the man. Keep him safe when shells in the air.
--------------------------------------------------------------------------------*/
Boolean doMan(void)
{
if (incoming_shells)
return false;
if (brainInfo->trees)
{
if (brainInfo->man_status || brainInfo->inboat)
return false;
if (buildPill())
return false;
if (buildRoad())
return false;
}
if (getTrees())
return false;
return false;
}
/*--------------------------------------------------------------------------------
If we need to then build a road to make thinks go quicker.
--------------------------------------------------------------------------------*/
Boolean buildRoad(void)
{
TERRAIN mapSpot;
mapSpot = terrainSpot();
if (mapSpot == RIVER || mapSpot == SWAMP || mapSpot == CRATER
|| mapSpot == RUBBLE || mapSpot == SWAMP)
{
if (mapSpot == RIVER || brainInfo->speed < 20)
{
brainInfo->build->action = BUILDMODE_ROAD;
brainInfo->build->x = brainInfo->tankx >> 8;
brainInfo->build->y = brainInfo->tanky >> 8;
return true;
}
}
return false;
}
/*--------------------------------------------------------------------------------
Build a pill near our base.
--------------------------------------------------------------------------------*/
Boolean buildPill(void)
{
if (!brainInfo->carriedpills)
return false;
if (!(brainInfo->armour <= 2 && incoming_shells)) /* if we are about to die, then build the thing */
{
if (!brainInfo->base) /* no base nearby */
return false;
if (brainInfo->base->info & OBJECT_HOSTILE)
return false;
if ((rangeFromMe(brainInfo->base) >> 8) > 4)
return false;
}
brainInfo->build->action = BUILDMODE_PBOX;
brainInfo->build->x = brainInfo->tankx >> 8;
brainInfo->build->y = brainInfo->tanky >> 8;
return true;
}
/*--------------------------------------------------------------------------------
If we are getting low on trees, then go get some.
--------------------------------------------------------------------------------*/
Boolean getTrees(void)
{
long range;
u_char x, y;
if (brainInfo->trees > 20)
return false;
if (terrainSpot() == FOREST)
{
brainInfo->build->action = BUILDMODE_FARM;
brainInfo->build->x = brainInfo->tankx >> 8;
brainInfo->build->y = brainInfo->tanky >> 8;
return true;
}
if (findObject(TERRAIN_MASK, FOREST, 6, 128, &x, &y))
{
brainInfo->build->action = BUILDMODE_FARM;
brainInfo->build->x = x;
brainInfo->build->y = y;
return true;
}
return false;
}
/*--------------------------------------------------------------------------------
Tally the shots comming at us.
--------------------------------------------------------------------------------*/
void countIncommingShots(void)
{
ObjectInfo *ob;
short n;
long dx, dy, minDist;
char dev;
// Count all shells coming towards us.
incoming_shells = 0;
minDist = 100000;
closestShot = nil;
for (n = 0; n < brainInfo->num_objects; ++n)
{
ob = &(brainInfo->objects[n]);
if (ob->object == OBJECT_SHOT)
{
/* dev = difference in the shots direction of travel and our
direction from the shot */
dx = (unsigned long)brainInfo->tankx - (unsigned long)ob->x;
dy = (unsigned long)brainInfo->tanky - (unsigned long)ob->y;
dev = ob->direction - aim(dx, dy);
if (dev > -45 && dev < 45)
{
++incoming_shells;
if (minDist > (dx * dx + dy * dy))
{
minDist = dx * dx + dy * dy;
closestShot = ob;
}
}
}
}
}
/*--------------------------------------------------------------------------------
Avoid the shots comming at us.
--------------------------------------------------------------------------------*/
Boolean avoidShots(void)
{
if ((incoming_shells > 4 || incoming_shells >= brainInfo->armour) && closestShot)
{
obliquelyAvoidSpace(closestShot->x, closestShot->y);
speedUp();
return true;
}
return false;
}
/*--------------------------------------------------------------------------------
If we are suddenly in water, then get out, and avoid it.
--------------------------------------------------------------------------------*/
Boolean avoidWater(void)
{
if (brainInfo->inboat)
return false;
if (terrainSpot() == RIVER)
{
/* #### perhaps build bridge here #### */
if (approachObject(TERRAIN_MASK, ROAD, 4, 30)
|| approachObject(TERRAIN_MASK, FOREST, 4, 30)
|| approachObject(TERRAIN_MASK, GRASS, 4, 30))
return speedUp();
}
if (avoidObject(TERRAIN_MASK, RIVER, 2))
return speedUp();
return false;
}
/*--------------------------------------------------------------------------------
Avoid map square.
--------------------------------------------------------------------------------*/
void avoidSpace(WORLD_X x, WORLD_Y y)
{
short direction;
direction = aim((unsigned long)x - (unsigned long)brainInfo->tankx,
(unsigned long)y - (unsigned long)brainInfo->tanky);
if (direction > brainInfo->direction && direction - brainInfo->direction < 128)
setkey(newKeys, KEY_turnleft);
else if (direction < 128 && brainInfo->direction > 128 + direction)
setkey(newKeys, KEY_turnleft);
else
setkey(newKeys, KEY_turnright);
}
/*--------------------------------------------------------------------------------
Avoid map square at an angle.
--------------------------------------------------------------------------------*/
void obliquelyAvoidSpace(WORLD_X x, WORLD_Y y)
{
short direction, da;
direction = aim((unsigned long)x - (unsigned long)brainInfo->tankx,
(unsigned long)y - (unsigned long)brainInfo->tanky);
if (direction > brainInfo->direction)
da = direction - brainInfo->direction;
else
da = brainInfo->direction - direction;
if (direction > brainInfo->direction && direction - brainInfo->direction < 128)
{
if (da < 90)
setkey(newKeys, KEY_turnleft);
}
else if (direction < 128 && brainInfo->direction > 128 + direction)
{
if (da < 90)
setkey(newKeys, KEY_turnleft);
}
else
{
if (da < 90)
setkey(newKeys, KEY_turnright);
}
}
/*--------------------------------------------------------------------------------
Approach map square.
--------------------------------------------------------------------------------*/
void approachSpace(WORLD_X x, WORLD_Y y)
{
short direction;
char da, max, min;
u_long *keysToSet;
long range;
direction = aim((unsigned long)x - (unsigned long)brainInfo->tankx,
(unsigned long)y - (unsigned long)brainInfo->tanky);
da = brainInfo->direction - direction;
range = (findrange(x, y, brainInfo->tankx, brainInfo->tanky)) >> 8;
/* if (range > 8)
{max = 7; min = 2;}
else if (range > 4)
{max = 10; min = 4;}
else if (range > 2)
{max = 20; min = 7;}
else
{max = 90; min = 45;}*/
/*if (range > 15)
range = 15;
max = (15 - range) * 3;
min = (15 - range);*/
max = 15;
min = 2;
if (da < max && da > -max)
keysToSet = &newTaps;
else
keysToSet = &newKeys;
if (da < min && da > -min)
return;
if (direction > brainInfo->direction && direction - brainInfo->direction < 128)
setkey(*keysToSet, KEY_turnright);
else if (direction < 128 && brainInfo->direction > 128 + direction)
setkey(*keysToSet, KEY_turnright);
else
setkey(*keysToSet, KEY_turnleft);
}
/*--------------------------------------------------------------------------------
If we are damaged, then avoid the pillboxes.
--------------------------------------------------------------------------------*/
Boolean avoidPill(void)
{
ObjectInfo *ob;
u_long range;
if (brainInfo->armour >= 4) /* do not avoid if we have armour */
return false;
for (ob = &brainInfo->objects[0]; ob < &brainInfo->objects[brainInfo->num_objects]; ob++)
{
if (ob->object == OBJECT_PILLBOX && (ob->info & OBJECT_HOSTILE))
{
if (ob->direction <= 2)
return false;
range = rangeFromMe(ob);
if ((range >> 8) > 8)
continue;
avoidSpace(ob->x, ob->y);
return speedUp();
}
}
return false;
}
/*--------------------------------------------------------------------------------
Avoid enemies
--------------------------------------------------------------------------------*/
Boolean avoidEnemy(void)
{
}
/*--------------------------------------------------------------------------------
Avoid nearby mines if we are moving, by turning and slowing.
--------------------------------------------------------------------------------*/
Boolean avoidMines(void)
{
u_char x, y;
Boolean mine;
if (!brainInfo->speed) /* we are stopped so no need to avoid */
return false;
mine = findObject(TERRAIN_MINE, TERRAIN_MINE, 2, 10, &x, &y);
if (!mine)
if (!findObject(TERRAIN_MINE, TERRAIN_MINE, 1, 50, &x, &y))
return false;
avoidSpace(x << 8, y << 8);
if (brainInfo->speed > MIN_SPEED)
return slowDown();
else
return speedUp();
}
/*--------------------------------------------------------------------------------
Avoid going into deep water, and get out of it when dead.
--------------------------------------------------------------------------------*/
Boolean avoidDeepWater(void)
{
if (terrainSpot() == DEEPSEA)
{
/*approachObject(DEEPSEA, 0, 14, 128);*/ /* anything but deepsea */
if (!approachObject(TERRAIN_MASK, ROAD, 14, 128))
if (!approachObject(TERRAIN_MASK, GRASS, 14, 128))
if (!approachObject(TERRAIN_MASK, FOREST, 14, 128))
if (!approachObject(TERRAIN_MASK, SWAMP, 14, 128))
if (!approachObject(TERRAIN_MASK, RUBBLE, 14, 128))
if (!approachObject(TERRAIN_MASK, HALFBUILDING, 14, 128))
if (!approachObject(TERRAIN_MASK, BUILDING, 14, 128))
approachObject(TERRAIN_MASK, CRATER, 14, 128);
return speedUp();
}
if (avoidObject(TERRAIN_MASK, DEEPSEA, 4))
return slowDown();
return false;
}
/*--------------------------------------------------------------------------------
Avoid nearby objects if we are moving, by turning away.
--------------------------------------------------------------------------------*/
Boolean avoidObject(u_char mask, TERRAIN thing, short distance)
{
u_char x, y;
if (!brainInfo->speed)
return false;
if (findObject(mask, thing, distance, 45, &x, &y))
{
avoidSpace(x << 8, y << 8);
return true;
}
return false; /* no evasion needed */
}
/*--------------------------------------------------------------------------------
Turn towards nearby objects.
--------------------------------------------------------------------------------*/
Boolean approachObject(u_char mask, TERRAIN thing, short distance, short da)
{
u_char x, y;
if (findObject(mask, thing, distance, da, &x, &y))
{
approachSpace(x << 8, y << 8);
return true;
}
return false;
}
/*--------------------------------------------------------------------------------
Find the thing in the map we can see.
--------------------------------------------------------------------------------*/
Boolean findObject(u_char mask, TERRAIN thing, short distance, short da, u_char *x, u_char *y)
{
short r;
BYTE angle;
WORLD_X wx;
WORLD_Y wy;
for (r = 1; r <= distance; ++r)
{
for (angle = 0; angle <= da; angle += 5)
{
wx = brainInfo->tankx + sin(brainInfo->direction + angle) * r;
wy = brainInfo->tanky - cos(brainInfo->direction + angle) * r;
if (wx >> 8 == brainInfo->tankx >> 8 && wy >> 8 == brainInfo->tanky >> 8)
continue;
if ((raw_getmapcell(wx >> 8, wy >> 8) & mask) == thing)
{
*x = wx >> 8;
*y = wy >> 8;
return true;
}
wx = brainInfo->tankx + sin(brainInfo->direction - angle) * r;
wy = brainInfo->tanky - cos(brainInfo->direction - angle) * r;
if (wx >> 8 == brainInfo->tankx >> 8 && wy >> 8 == brainInfo->tanky >> 8)
continue;
if ((raw_getmapcell(wx >> 8, wy >> 8) & mask) == thing)
{
*x = wx >> 8;
*y = wy >> 8;
return true;
}
}
}
return false;
}
/*--------------------------------------------------------------------------------
Return the terrain info from the map location near us.
--------------------------------------------------------------------------------*/
TERRAIN mapNearUs(u_char direction, char distance)
{
WORLD_X wx, wy;
wx = brainInfo->tankx + sin(direction) * distance;
wy = brainInfo->tanky - cos(direction) * distance;
return raw_getmapcell(wx >> 8, wy >> 8);
}
/*--------------------------------------------------------------------------------
Return the terrain info from the map.
--------------------------------------------------------------------------------*/
TERRAIN raw_getmapcell(MAP_X x, MAP_Y y)
{
if (x < brainInfo->view_left || y < brainInfo->view_top)
return(DEEPSEA);
y -= brainInfo->view_top;
x -= brainInfo->view_left;
if (x >= brainInfo->view_width || y >= brainInfo->view_height)
return(DEEPSEA);
return (brainInfo->viewdata[y * brainInfo->view_width + x]);
}
/*--------------------------------------------------------------------------------
Send a debug msg to ourselves.
--------------------------------------------------------------------------------*/
void sendDebugMsg(short msgIndex, char *theMessage, ...)
{
short x;
char buffer[256];
va_list ptr;
*(brainInfo->messagedest) = 0xFFFFFFFF;
if (!brainInfo->sendmessage[0])
{
if (TickCount() < lastMsg)
return;
if (msgIndex >= MAX_MSGS)
return;
if (TickCount() - msgTimes[msgIndex] <= MIN_WAIT)
return;
msgTimes[msgIndex] = TickCount();
lastMsg = TickCount() + 60;
va_start(ptr, theMessage);
buffer[0] = vsprintf((char *)buffer+1, theMessage, ptr);
for (x = 0; x <= buffer[0]; ++x)
brainInfo->sendmessage[x] = buffer[x];
}
}
/*--------------------------------------------------------------------------------
Find the distance between two objects
--------------------------------------------------------------------------------*/
u_long findrange(long x1, long y1, long x2, long y2)
{
long dx, dy;
dx = x1 - x2;
dy = y1 - y2;
return (FracSqrt(dx * dx + dy * dy) >> 15);
}
/*--------------------------------------------------------------------------------
return a number between min - 1 and max
--------------------------------------------------------------------------------*/
unsigned short rnd(short min, short max)
{
unsigned short ranNum;
long range, t;
ranNum = Random();
range = max - min;
t = (ranNum * range) / 65536;
return (t + min);
}
/*--------------------------------------------------------------------------------
Slow down.
--------------------------------------------------------------------------------*/
Boolean slowDown(void)
{
setkey(newKeys, KEY_slower);
clearkey(newKeys, KEY_faster);
return true;
}
/*--------------------------------------------------------------------------------
Speed up.
--------------------------------------------------------------------------------*/
Boolean speedUp(void)
{
setkey(newKeys, KEY_faster);
clearkey(newKeys, KEY_slower);
return true;
}
/*--------------------------------------------------------------------------------
The terrain at the spot.
--------------------------------------------------------------------------------*/
TERRAIN terrainSpot(void)
{
return raw_getmapcell(brainInfo->tankx >> 8, brainInfo->tanky >> 8) & TERRAIN_MASK;
}